﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Battleship
{
    class Formulas
    {
        /*
         * Hàm tính nội suy Lagrange 
         */
        public static double Lagrange(double x, double[] X, double[] Y)
        {
            if (X.Length != Y.Length || X.Length < 2) { return 0; }
            double result = 0;
            double volume;
            for (int i = 0; i < X.Length; i++)
            {
                volume = 1;
                for (int j = 0; j < X.Length; j++)
                {
                    if (j != i) { volume *= (x - X[j]) / (X[i] - X[j]); }
                }
                result += Y[i] * volume;
            }
            return result;
        }

        /*
         * Hàm tính nội suy Newton tổng quát 
         */
        public static double Newton(double x, double[] X, double[] Y)
        {
            if (X.Length != Y.Length || X.Length < 2) { return 0; }
            // Số sai phân cần tính
            int numOfDifference = X.Length - 1;
            double[] SP = new double[numOfDifference];
            List<double> coefficients = new List<double>();

            // Hệ số đầu tiên bằng Y[0]
            coefficients.Add(Y[0]);
            double result = coefficients[0];

            // Tính sai phân bậc 1
            for (int i = 0; i < numOfDifference; i++)
            {
                SP[i] = (Y[i + 1] - Y[i]) / (X[i + 1] - X[i]);
            }
            coefficients.Add(SP[0]);

            for (int k = 2; k <= numOfDifference; k++)
            {
                // Hệ số bằng phần tử đầu tiên của sai phân
                coefficients.Add(Difference(SP, k, X)[0]);
            }
            
            for (int i = 1; i < coefficients.Count(); i++)
            {
                for (int j = 0; j < i; j++) 
                { 
                    result += coefficients[i] * (x - X[j]); 
                }
            }
            return result;
        }

        /**
         * Hàm tính sai phân Newton bậc k (chỉ tính từ bậc 2 trở lên)
         * Muốn gọi hàm phải tính sai phân bậc 1 trước 
         **/
        public static double[] Difference(double[] SP, int k, double[] X)
        {
            if (SP.Length > 0 && k >= 2)
            {
                for (int i = 0; i < X.Length - k; i++)
                {
                    SP[i] = (SP[i + 1] - SP[i]) / (X[i + k] - X[i]);
                }
                return SP;
            }
            else
            {
                return new double[] {};
            }
        }

        /**
         * Hàm nội suy tuyến tính 
         * Dùng để tính biên độ lắc
         */
        public static double linearInterpolation(double x, double[] X, double[] Y)
        {
            double result = 0;
            for (int i = 0; i < X.Length - 1; i++)
            {
                result = (x == X[i]) ? Y[i] : result;
                if (x > X[i] && x < X[i + 1])
                {
                    if (Y[i] < Y[i + 1])
                    {
                        result = ((Y[i + 1] - Y[i]) / Math.Abs((X[i + 1] - X[i]))) * (x - X[i]) + Y[i];
                    }
                    else
                    {
                        result = Y[i] - ((Y[i] - Y[i + 1]) / Math.Abs((X[i + 1] - X[i]))) * (x - X[i]);
                    }
                }
            }
            return result;
        }
        
        /**
         * Tính chiều chìm phía lái
         * Bảng HYDRO và BONJEAN
         */
        public static double calDao(double d, double L, double LCF,
            double LCG, double LCB, double BML)
        {
            return d + (L / 2 - LCF) * (LCG - LCB) / BML;
        }

        /**
         * Tính chiều chìm phía mũi
         * Bảng HYDRO và BONJEAN
         */
        public static double calDfo(double d, double L, double LCF,
            double LCG, double LCB, double BML)
        {
            return d - (L / 2 - LCF) * (LCG - LCB) / BML;
        }

        /**
         * Hàm tính chiều chìm
         * Bảng HYDRO và BONJEAN
         */
        public static double[] calDraft(double da, double df, double Lpp, double[] suon)
        {
            double[] d = new double[suon.Length];
            d[0] = df;
            double tan_a = (da - df) / Lpp;
            for (int i = 1; i < d.Length; i++)
            {
                d[i] = df + tan_a * (suon[i] - suon[i - 1]);
            }
            return d;
        }
    }
}
